package main

import "strings"

// 执行用时：0 ms, 在所有 Go 提交中击败了100.00% 的用户
// 内存消耗：1.9 MB, 在所有 Go 提交中击败了100.00% 的用户
// 和官方一样的思路，枚举， 代码很长~ 双百通过，哈哈
// 就是在满足游戏结束之前， 看棋盘是否能下成给定的样子
// 下满。或者 任意行列或对角线三个相同游戏结束。

// 按照官方的提示，枚举
func validTicTacToe(board []string) bool {
	// 1. 统计数量
	cntX, cntO := 0, 0
	for _, s := range board {
		for i := 0; i < 3; i++ {
			if s[i] == 'X' {
				cntX += 1
			} else if s[i] == 'O' {
				cntO += 1
			}
		}
	}
	// 2. 分情况讨论，获胜的情况
	x, o := isBate(board, 'X'), isBate(board, 'O')
	if x && o { // 如果都对返回错误
		return false
	} else if x { // 如果 X胜， 必须 X 比 O多下一颗棋，否则返回错误
		if cntX == cntO+1 {
			return true
		}
		return false
	} else if o { // 同理 O胜， 必须 X 和 O 下棋一样多，否则返回错误
		if cntX == cntO {
			return true
		}
		return false
	} else { // 最后，没有胜出的情况， 必须满足棋子的数量关系
		// 如果 cntX < cntO 或 cntX 大于 2 cntO 返回错误
		if cntX >= cntO && cntX-cntO <= 1 {
			return true
		}
	}
	return false
}

// 用来判断是否有胜出
func isBate(board []string, c byte) bool {
	// 1. 先看每一行是否可以
	for _, s := range board {
		isTrue := true
		for j := 0; j < 3; j++ {
			if s[j] != c {
				isTrue = false
				break
			}
		}
		if isTrue {
			return true
		}
	}
	// 2. 每一列
	for i := 0; i < 3; i++ {
		isTrue := true
		for _, s := range board {
			if s[i] != c {
				isTrue = false
				break
			}
		}
		if isTrue {
			return true
		}
	}
	// // 3. 对角线, 正对角线
	for i, s := range board {
		if s[i] != c {
			break
		}
		if i == len(board)-1 {
			return true
		}
	}
	// // 对角线
	n := len(board)
	for i, s := range board {
		if s[n-i-1] != c {
			break
		}
		if n-i-1 == 0 {
			return true
		}
	}
	return false
}

// 官方代码，如此的简洁！很多地方值得优化学习
func win(board []string, p byte) bool {
	for i := 0; i < 3; i++ {
		if board[i][0] == p && board[i][1] == p && board[i][2] == p ||
			board[0][i] == p && board[1][i] == p && board[2][i] == p {
			return true
		}
	}
	return board[0][0] == p && board[1][1] == p && board[2][2] == p ||
		board[0][2] == p && board[1][1] == p && board[2][0] == p
}

func validTicTacToe(board []string) bool {
	oCount, xCount := 0, 0
	for _, row := range board {
		// 使用了一个内置方法
		oCount += strings.Count(row, "O")
		xCount += strings.Count(row, "X")
	}
	return !(oCount != xCount && oCount != xCount-1 ||
		oCount != xCount && win(board, 'O') ||
		oCount != xCount-1 && win(board, 'X'))
}
